前言

这几篇文章是根据谷歌出的Android性能的专题视频的总结以及实验的笔记,这个视频主题从Render渲染、compute算法、memory算法、battery电池这几个方面来讲述如何对Android应用进行性能上的优化。主要是介绍了一些检测的工具,和优化的策略。

#Render#

CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染。
每次从CPU转移到GPU是一件很麻烦的事情,OpenGL ES可以把那些需要渲染的纹理Hold在GPU Memory里面,在下次需要渲染的时候直接进行操作
在Android里面那些由主题所提供的资源,例如Bitmaps,Drawables都是一起打包到统一的Texture纹理当中,然后再传递到 GPU里面
当然随着UI组件的越来越丰富,有了更多演变的形态。例如显示图 片的时候,需要先经过CPU的计算加载到内存中,然后传递给GPU进行渲染。文字的显示更加复杂,需要先经过CPU换算成纹理,然后再交给GPU进行渲 染,回到CPU绘制单个字符之后,再重新引用经过GPU渲染的内容。动画则是一个更加复杂的操作流程。

卡顿原因

手机的刷新频率为60fps,如果当前需要处理的事情太多了,以至于无法再16ms内完成frame的刷新,就造成了丢帧,在视觉上,就给用户卡顿的感觉。如果此时用户正在进行一些交互,比如滑动屏幕、输入文字等,这种卡顿就会尤为明显。

另外,虚拟机在执行GC垃圾回收操作时所有线程(包括UI线程)都需要暂停,当GC垃圾回收完成之后所有线程才能够继续执行。所以如果此时有大量的GC操作,也会引起卡顿问题。

UI性能优化策略

  1. CPU side: 不必要的layout和invalidations。过多的view hierarchy和不必要的重绘导致CPU过度的刷新displaylist和相关的GPU资源

  2. GPU side: overdraw。

Overdraw

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次
在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。

overdraw检测:在手机的开发者选项中,选择’调试GPU过度绘制’->’显示过度绘制区域’

我们的目标是,减少红色区域的面积大小

示例一:

去除background

对应代码,查看是否可以减少红色区域的面积

<FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@color/white">

        <include layout="@layout/main_content" />

        <com.zowee.lib.widget.HeaderLayout
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/header_hl"
            android:layout_width="match_parent"
            android:layout_height="@dimen/title_height"
            android:background="@color/main_title_bg"
            app:hlNavigationIcon="@drawable/main_menu_btn"
            app:hlNavigationMinWidth="@dimen/title_height"
            app:hlNavigationScaleType="centerInside"
            app:hlSupportTranslucentStatus="true"
            app:hlTitleText="@string/main_title" />
    </FrameLayout>

发现代码中可能之前无意加的 ‘android:background=”@color/white”‘,把背景去掉试试

果然红色区域去掉了~

官方示例:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // Don't draw anything until all the Asynctasks are done and all the DroidCards are ready.
    if (mDroids.length > 0 && mDroidCards.size() == mDroids.length) {
        // Loop over all the droids, except the last one.
        int i;

        for (i = 0; i < mDroidCards.size()-1; i++) {
            // Each card is laid out a little to the right of the previous one.
            mCardLeft = i * mCardSpacing;
            // Save the canvas state
            canvas.save();

            // Restrict the drawing area to what is visible
            canvas.clipRect(mCardLeft, 0, mCardLeft+mCardSpacing, mDroidCards.get(i).getHeight());

            drawDroidCard(canvas, mDroidCards.get(i), mCardLeft, 0);

            // Restore canvas to non-clipping state
            canvas.restore();
        }
        // Draw the final card without clipping
        drawDroidCard(canvas, mDroidCards.get(i), mCardLeft + mCardSpacing, 0);
    }
    // Invalidate the whole view. Doing this calls onDraw() if the view is visible.
    invalidate();
}

canvas.clipRect(mCardLeft, 0, mCardLeft+mCardSpacing, mDroidCards.get(i).getHeight()); 指明画布的可见区域。对比clipRect后,避免过度重绘的效果

Hierachy

Hierachy用于查看界面的层级结构。

尽量减少界面的层级结构,尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局,尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。

Lint

代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测

检测结果:

UI性能分析解决总结

  • 布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。
  • 列表及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。
  • 背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现。
  • 自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。
  • canvas.clipRect 限制draw的可见区域